This is an R Markdown
Notebook. Each section of the code is then explained.
First of all import the libraries needed
#install.packages(c("datavolley", "ovlytics"))
library(datavolley)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
library(ggplot2)
Avvertimento: il pacchetto ‘ggplot2’ è stato creato con R versione 4.3.2
library(dplyr)
Avvertimento: il pacchetto ‘dplyr’ è stato creato con R versione 4.3.2
Caricamento pacchetto: ‘dplyr’
I seguenti oggetti sono mascherati da ‘package:stats’:
filter, lag
I seguenti oggetti sono mascherati da ‘package:base’:
intersect, setdiff, setequal, union
library(ovlytics)
Import the file you are interested in considering more than one
match, you have to import all the folder
filename <- "C:/Users/mirko/Documents/GitHub/CuneoWebsite.io/Assets/Cuneo-Bergamo_cuneo.dvw"
#d <- dir("C:/Users/mirko/OneDrive - Politecnico di Milano/Altro/Volley/Conco2324/Parella Torino/Ritorno/", pattern = "dvw$", full.names = TRUE)
teamName = 'HONDA OLIVERO S.BERNARDO CUNEO'
x <- dv_read(filename)
Avvertimento: stri_enc_detect2 is deprecated and will be removed in a future release of 'stringi'.
serve_idx <- find_serves(plays(x))
table(plays(x)$team[serve_idx])
HONDA OLIVERO S.BERNARDO CUNEO VOLLEY BERGAMO 1991
98 93
Funzioni utili
## find rows where a single player is on court
player_on_court <- function(x, target_player_id, team = NULL) {
if (!is.null(team)) team <- match.arg(team, c("home", "visiting"))
## 'team' is optional here, if NULL then we look at both home and visiting teams
idx <- rep(FALSE, nrow(x))
if (is.null(team) || team == "home") {
idx <- idx | x$home_player_id1 == target_player_id | x$home_player_id2 == target_player_id | x$home_player_id3 == target_player_id |
x$home_player_id4 == target_player_id | x$home_player_id5 == target_player_id | x$home_player_id6 == target_player_id
}
if (is.null(team) || team == "visiting") {
idx <- idx | x$visiting_player_id1 == target_player_id | x$visiting_player_id2 == target_player_id | x$visiting_player_id3 == target_player_id |
x$visiting_player_id4 == target_player_id | x$visiting_player_id5 == target_player_id | x$visiting_player_id6 == target_player_id
}
idx[is.na(idx)] <- FALSE
idx
}
## find rows where any of our target players are on court
any_player_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ANY of those players were on court
apply(do.call(cbind, out), 1, any)
}
## find rows where all of our target players are on court
all_players_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ALL of those players were on court
apply(do.call(cbind, out), 1, all)
}
d <- dir("C:/Users/mirko/Documents/GitHub/CuneoWebsite.io/Assets/", pattern = "dvw$", full.names = TRUE)
lx <- list()
## read each file
for (fi in seq_along(d)) lx[[fi]] <- dv_read(d[fi], insert_technical_timeouts = FALSE)
Avvertimento: stri_enc_detect2 is deprecated and will be removed in a future release of 'stringi'.
## now extract the play-by-play component from each and bind them together
px <- list()
for (fi in seq_along(lx)) px[[fi]] <- plays(lx[[fi]])
px <- do.call(rbind, px)
Rendimento in Battuta
#, end_zone == 5
library("formattable")
Avvertimento: il pacchetto ‘formattable’ è stato creato con R versione 4.3.3Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
table_data <- px %>%
dplyr::filter(skill == "Serve", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_battute = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_battute, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_battute, digits = 0),
)
data_plot <- table_data
table_data
# Calculate cumulative statistics for the team
team_total <- table_data %>%
summarise(
N_battute = sum(N_battute),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_battute), digits = 0),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_battute), digits = 0)
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Combine the team total row with the original table data
table_data_with_total <- bind_rows(table_data, team_total)
# Print the table with the team total row
table_data_with_total
library(kableExtra)
Avvertimento: il pacchetto ‘kableExtra’ è stato creato con R versione 4.3.2
Caricamento pacchetto: ‘kableExtra’
Il seguente oggetto è mascherato da ‘package:dplyr’:
group_rows
# Apply custom CSS styling to the entire table
# Reorder columns to make positività the second column
table_data <- table_data_with_total %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(9, bold = TRUE) %>%
column_spec(2, background = ifelse(table_data$efficienza >= percent(0.3), "lightgreen",ifelse(table_data$efficienza > percent(0.25) & table_data$efficienza < percent(0.3), "yellow", "lightcoral")))
#row_spec(which(table_data$efficienza > 0.2 & table_data$efficienza < 0.3), background = "yellow") %>%
#row_spec(which(table_data$efficienza <= 0.2), background = "lightcoral")
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Battuta_tab.html")
library(plotly)
Avvertimento: il pacchetto ‘plotly’ è stato creato con R versione 4.3.2
Caricamento pacchetto: ‘plotly’
Il seguente oggetto è mascherato da ‘package:formattable’:
style
Il seguente oggetto è mascherato da ‘package:ggplot2’:
last_plot
Il seguente oggetto è mascherato da ‘package:stats’:
filter
Il seguente oggetto è mascherato da ‘package:graphics’:
layout
fig <- plot_ly(data_plot,
x = ~positività*100,
y = ~efficienza*100,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività (%)</b>: %{x})',
'<br><b>Efficienza (%)</b>: %{y}',
'<br><b>Battuta</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_battute, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Battuta',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
# Assuming you have your data frame 'data_plot' with columns 'player_name' and 'efficienza'
# Calculate the color based on efficienza values
data_plot$color <- ifelse(data_plot$efficienza < 0.25, "red",
ifelse(data_plot$efficienza > 0.30, "green", "yellow"))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Battuta",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Battuta",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
# Customize other plot settings as needed
library(htmlwidgets)
Avvertimento: il pacchetto ‘htmlwidgets’ è stato creato con R versione 4.3.2
# Assuming 'fig' is your Plotly figure
saveWidget(fig, "Battuta.html")
Rendimento in Ricezione
Ora analizziamo la ricezione:
#, end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Reception", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_receptions = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_receptions, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_receptions, digits = 0),
)
data_plot <- table_data
table_data
# Compute total statistics for the team
total_stats <- table_data %>%
summarise(
N_receptions = sum(N_receptions),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_receptions)),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_receptions))
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Combine the team total row with the original table data
table_data_with_total <- bind_rows(table_data, total_stats)
# Print the table with the team total row
table_data_with_total
table_data <- table_data_with_total %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(7, bold = TRUE) %>%
column_spec(2,
background = case_when(
(table_data$player_name == "Serena Scognamillo" | table_data$player_name == "Federica Ferrario") ~ ifelse(table_data$efficienza >= percent(0.56), "lightgreen", ifelse(table_data$efficienza > percent(0.48) & table_data$efficienza < percent(0.56), "yellow", "lightcoral")),
(table_data$player_name == "Lena Stigrot" | table_data$player_name == "Anna Haak") ~ ifelse(table_data$efficienza >= percent(0.37), "lightgreen", ifelse(table_data$efficienza > percent(0.31) & table_data$efficienza < percent(0.37), "yellow", "lightcoral")),
(table_data$player_name == "Alice Tanase" | table_data$player_name == "Madison Kubik") ~ ifelse(table_data$efficienza >= percent(0.43), "lightgreen", ifelse(table_data$efficienza > percent(0.37) & table_data$efficienza < percent(0.43), "yellow", "lightcoral")),
TRUE ~ ifelse(table_data$efficienza >= percent(0.42), "lightgreen", ifelse(table_data$efficienza > percent(0.41) & table_data$efficienza < percent(0.42), "orange", "red"))
)
)
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Ricezione_tab.html")
fig <- plot_ly(data_plot,
x = ~positività*100,
y = ~efficienza*100,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività(%)</b>: %{x}',
'<br><b>Efficienza(%)</b>: %{y}',
'<br><b>Ricezione</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_receptions, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Ricezione',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
# Assuming you have your data frame 'data_plot' with columns 'player_name' and 'efficienza'
# Calculate the color based on efficienza values
data_plot <- table_data %>%
mutate(color = case_when(
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza >= percent(0.56)) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza >= percent(0.37)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza >= percent(0.43)) |
efficienza >= percent(0.42) ~ "lightgreen",
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza > percent(0.48) & efficienza < percent(0.56)) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza > percent(0.31) & efficienza < percent(0.37)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza > percent(0.37) & efficienza < percent(0.43)) |
(efficienza > percent(0.41) & efficienza < percent(0.42)) ~ "orange",
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza <= percent(0.48) ) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza <= percent(0.31)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza <= percent(0.37)) |
efficienza < percent(0.41) ~ "lightcoral",
TRUE ~ NA_character_
))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Ricezione",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Ricezione",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
saveWidget(fig, "Ricezione.html")
Rendimento in Attacco
# end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Attack", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_attacks = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_attacks, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_attacks, digits = 0),
)
data_plot <- table_data
table_data
# Compute the total statistics
total_stats <- table_data %>%
summarise(
N_attacks = sum(N_attacks),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_attacks)),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_attacks))
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Add the total row to the table data
table_data <- bind_rows(table_data, total_stats)
table_data <- table_data %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
column_spec(2, background = case_when(
(table_data$player_name == "Anna Adelusi" | table_data$player_name == "Terry Ruth Enweonwu") ~ ifelse(table_data$efficienza >= percent(0.27), "lightgreen", ifelse(table_data$efficienza > percent(0.24) & table_data$efficienza < percent(0.27), "yellow", "lightcoral")),
(table_data$player_name == "Anna Haak" | table_data$player_name == "Lena Stigrot") ~ ifelse(table_data$efficienza >= percent(0.34), "lightgreen", ifelse(table_data$efficienza > percent(0.30) & table_data$efficienza < percent(0.34), "yellow", "lightcoral")),
(table_data$player_name == "Alice Tanase" | table_data$player_name == "Madison Kubik") ~ ifelse(table_data$efficienza >= percent(0.24), "lightgreen", ifelse(table_data$efficienza > percent(0.20) & table_data$efficienza < percent(0.24), "yellow", "lightcoral")),
(table_data$player_name == "Saly Thior" | table_data$player_name == "Amandha Sylves" | table_data$player_name == "Anna Hall" | table_data$player_name == "Beatrice Molinaro") ~ ifelse(table_data$efficienza >= percent(0.44), "lightgreen", ifelse(table_data$efficienza > percent(0.38) & table_data$efficienza < percent(0.44), "yellow", "lightcoral")),
TRUE ~ ifelse(table_data$efficienza >= percent(0.42), "lightgreen", ifelse(table_data$efficienza > percent(0.41) & table_data$efficienza < percent(0.42), "orange", "red"))
)
)
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Attacco_tab.html")
fig <- plot_ly(data_plot,
x = ~positività,
y = ~efficienza,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività(%)</b>: %{x}',
'<br><b>Efficienza(%)</b>: %{y}',
'<br><b>Attacchi</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_attacks, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Attacco',
xaxis = list(title = 'Positività', showgrid = FALSE),
yaxis = list(title = 'Efficienza', showgrid = FALSE)
)
fig
# Add a new column 'color' based on the rules provided
data_plot <- table_data %>%
mutate(color = case_when(
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza >= percent(0.27)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza >= percent(0.34)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza >= percent(0.24)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza >= percent(0.44)) |
efficienza >= percent(0.42) ~ "lightgreen",
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza > percent(0.24) & efficienza < percent(0.27)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza > percent(0.30) & efficienza < percent(0.34)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza > percent(0.20) & efficienza < percent(0.24)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza > percent(0.38) & efficienza < percent(0.44)) |
(efficienza > percent(0.41) & efficienza < percent(0.42)) ~ "orange",
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza <= percent(0.24)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza <= percent(0.30)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza <= percent(0.20)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza <= percent(0.38)) |
efficienza < percent(0.41) ~ "lightcoral",
TRUE ~ NA_character_
))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Attacco",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Attacco",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
saveWidget(fig, "Attacco.html")
Volleyball Distribution
library(ggplot2)
library(htmlwidgets)
for (i in 1:6) {
attack_rate <- px %>%
dplyr::filter(skill == "Attack", team == teamName, visiting_setter_position == i) %>%
group_by(start_zone) %>%
dplyr::summarize(n_attacks = n()) %>%
mutate(rate = n_attacks/sum(n_attacks)) %>%
ungroup
attack_rate <- cbind(attack_rate, dv_xy(attack_rate$start_zone, end = "lower"))
tm2i <- attack_rate$team == teams(px)[2]
attack_rate[tm2i, c("x", "y")] <- dv_flip_xy(attack_rate[tm2i, c("x", "y")])
attack_rate$rate_rounded <- round(attack_rate$rate, 2)
fig2 <- ggplot(attack_rate, aes(x, y, fill = rate_rounded)) +
geom_tile() +
geom_text(aes(label = paste0(round(rate_rounded * 100, 2), "%")), color = "black", size = 3) +
ggcourt(labels = teams(px)) +
scale_fill_gradient2(name = "Attack rate (%)", labels = scales::percent_format(accuracy = 0.01))
html_name <- paste0("Distribuzione", i, ".png")
# Save the plot as an HTML file
ggsave(html_name, plot = fig2, width = 8, height = 6, units = "in", dpi = 300)
}
LS0tDQp0aXRsZTogIkN1bmVvIERhdGEgQW5hbHlzaXMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIEVhY2ggc2VjdGlvbiBvZiB0aGUgY29kZSBpcyB0aGVuIGV4cGxhaW5lZC4NCg0KRmlyc3Qgb2YgYWxsIGltcG9ydCB0aGUgbGlicmFyaWVzIG5lZWRlZA0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKGMoImRhdGF2b2xsZXkiLCAib3ZseXRpY3MiKSkNCmxpYnJhcnkoZGF0YXZvbGxleSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG92bHl0aWNzKQ0KYGBgDQoNCkltcG9ydCB0aGUgZmlsZSB5b3UgYXJlIGludGVyZXN0ZWQgaW4gY29uc2lkZXJpbmcgbW9yZSB0aGFuIG9uZSBtYXRjaCwgeW91IGhhdmUgdG8gaW1wb3J0IGFsbCB0aGUgZm9sZGVyDQoNCmBgYHtyfQ0KZmlsZW5hbWUgPC0gIkM6L1VzZXJzL21pcmtvL0RvY3VtZW50cy9HaXRIdWIvQ3VuZW9XZWJzaXRlLmlvL0Fzc2V0cy9DdW5lby1CZXJnYW1vX2N1bmVvLmR2dyINCiNkIDwtIGRpcigiQzovVXNlcnMvbWlya28vT25lRHJpdmUgLSBQb2xpdGVjbmljbyBkaSBNaWxhbm8vQWx0cm8vVm9sbGV5L0NvbmNvMjMyNC9QYXJlbGxhIFRvcmluby9SaXRvcm5vLyIsIHBhdHRlcm4gPSAiZHZ3JCIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KdGVhbU5hbWUgPSAnSE9OREEgT0xJVkVSTyBTLkJFUk5BUkRPIENVTkVPJw0KeCA8LSBkdl9yZWFkKGZpbGVuYW1lKQ0Kc2VydmVfaWR4IDwtIGZpbmRfc2VydmVzKHBsYXlzKHgpKQ0KdGFibGUocGxheXMoeCkkdGVhbVtzZXJ2ZV9pZHhdKQ0KYGBgDQoNCkZ1bnppb25pIHV0aWxpDQoNCmBgYHtyfQ0KIyMgZmluZCByb3dzIHdoZXJlIGEgc2luZ2xlIHBsYXllciBpcyBvbiBjb3VydA0KcGxheWVyX29uX2NvdXJ0IDwtIGZ1bmN0aW9uKHgsIHRhcmdldF9wbGF5ZXJfaWQsIHRlYW0gPSBOVUxMKSB7DQogIGlmICghaXMubnVsbCh0ZWFtKSkgdGVhbSA8LSBtYXRjaC5hcmcodGVhbSwgYygiaG9tZSIsICJ2aXNpdGluZyIpKQ0KICAjIyAndGVhbScgaXMgb3B0aW9uYWwgaGVyZSwgaWYgTlVMTCB0aGVuIHdlIGxvb2sgYXQgYm90aCBob21lIGFuZCB2aXNpdGluZyB0ZWFtcw0KICBpZHggPC0gcmVwKEZBTFNFLCBucm93KHgpKQ0KICBpZiAoaXMubnVsbCh0ZWFtKSB8fCB0ZWFtID09ICJob21lIikgew0KICAgIGlkeCA8LSBpZHggfCB4JGhvbWVfcGxheWVyX2lkMSA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCRob21lX3BsYXllcl9pZDIgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkaG9tZV9wbGF5ZXJfaWQzID09IHRhcmdldF9wbGF5ZXJfaWQgfA0KICAgICAgICAgICAgICAgICB4JGhvbWVfcGxheWVyX2lkNCA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCRob21lX3BsYXllcl9pZDUgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkaG9tZV9wbGF5ZXJfaWQ2ID09IHRhcmdldF9wbGF5ZXJfaWQNCiAgfQ0KICBpZiAoaXMubnVsbCh0ZWFtKSB8fCB0ZWFtID09ICJ2aXNpdGluZyIpIHsNCiAgICBpZHggPC0gaWR4IHwgeCR2aXNpdGluZ19wbGF5ZXJfaWQxID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JHZpc2l0aW5nX3BsYXllcl9pZDIgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkMyA9PSB0YXJnZXRfcGxheWVyX2lkIHwNCiAgICAgICAgICAgICAgICAgeCR2aXNpdGluZ19wbGF5ZXJfaWQ0ID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JHZpc2l0aW5nX3BsYXllcl9pZDUgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkNiA9PSB0YXJnZXRfcGxheWVyX2lkDQogIH0NCiAgaWR4W2lzLm5hKGlkeCldIDwtIEZBTFNFDQogIGlkeA0KfQ0KDQojIyBmaW5kIHJvd3Mgd2hlcmUgYW55IG9mIG91ciB0YXJnZXQgcGxheWVycyBhcmUgb24gY291cnQNCmFueV9wbGF5ZXJfb25fY291cnQgPC0gZnVuY3Rpb24oeCwgdGFyZ2V0X3BsYXllcl9pZHMsIHRlYW0gPSBOVUxMKSB7DQogICMjIGZvciBlYWNoIHRhcmdldCBwbGF5ZXIsIGZpbmQgcm93cyB3aGVyZSB0aGV5IGFyZSBvbiBjb3VydA0KICBvdXQgPC0gbGFwcGx5KHRhcmdldF9wbGF5ZXJfaWRzLCBmdW5jdGlvbihwaWQpIHBsYXllcl9vbl9jb3VydCh4LCB0YXJnZXRfcGxheWVyX2lkID0gcGlkLCB0ZWFtID0gdGVhbSkpDQogICMjIGFuZCBub3cgZmluZCByb3dzIHdoZXJlIEFOWSBvZiB0aG9zZSBwbGF5ZXJzIHdlcmUgb24gY291cnQNCiAgYXBwbHkoZG8uY2FsbChjYmluZCwgb3V0KSwgMSwgYW55KQ0KfQ0KDQojIyBmaW5kIHJvd3Mgd2hlcmUgYWxsIG9mIG91ciB0YXJnZXQgcGxheWVycyBhcmUgb24gY291cnQNCmFsbF9wbGF5ZXJzX29uX2NvdXJ0IDwtIGZ1bmN0aW9uKHgsIHRhcmdldF9wbGF5ZXJfaWRzLCB0ZWFtID0gTlVMTCkgew0KICAjIyBmb3IgZWFjaCB0YXJnZXQgcGxheWVyLCBmaW5kIHJvd3Mgd2hlcmUgdGhleSBhcmUgb24gY291cnQNCiAgb3V0IDwtIGxhcHBseSh0YXJnZXRfcGxheWVyX2lkcywgZnVuY3Rpb24ocGlkKSBwbGF5ZXJfb25fY291cnQoeCwgdGFyZ2V0X3BsYXllcl9pZCA9IHBpZCwgdGVhbSA9IHRlYW0pKQ0KICAjIyBhbmQgbm93IGZpbmQgcm93cyB3aGVyZSBBTEwgb2YgdGhvc2UgcGxheWVycyB3ZXJlIG9uIGNvdXJ0DQogIGFwcGx5KGRvLmNhbGwoY2JpbmQsIG91dCksIDEsIGFsbCkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0KZCA8LSBkaXIoIkM6L1VzZXJzL21pcmtvL0RvY3VtZW50cy9HaXRIdWIvQ3VuZW9XZWJzaXRlLmlvL0Fzc2V0cy8iLCBwYXR0ZXJuID0gImR2dyQiLCBmdWxsLm5hbWVzID0gVFJVRSkNCmx4IDwtIGxpc3QoKQ0KIyMgcmVhZCBlYWNoIGZpbGUNCmZvciAoZmkgaW4gc2VxX2Fsb25nKGQpKSBseFtbZmldXSA8LSBkdl9yZWFkKGRbZmldLCBpbnNlcnRfdGVjaG5pY2FsX3RpbWVvdXRzID0gRkFMU0UpDQojIyBub3cgZXh0cmFjdCB0aGUgcGxheS1ieS1wbGF5IGNvbXBvbmVudCBmcm9tIGVhY2ggYW5kIGJpbmQgdGhlbSB0b2dldGhlcg0KcHggPC0gbGlzdCgpDQpmb3IgKGZpIGluIHNlcV9hbG9uZyhseCkpIHB4W1tmaV1dIDwtIHBsYXlzKGx4W1tmaV1dKQ0KcHggPC0gZG8uY2FsbChyYmluZCwgcHgpDQoNCmBgYA0KDQojIyBSZW5kaW1lbnRvIGluIEJhdHR1dGENCg0KYGBge3J9DQojLCBlbmRfem9uZSA9PSA1DQpsaWJyYXJ5KCJmb3JtYXR0YWJsZSIpIA0KdGFibGVfZGF0YSA8LSBweCAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc2tpbGwgPT0gIlNlcnZlIiwgdGVhbSA9PSB0ZWFtTmFtZSkgJT4lIA0KICBncm91cF9ieShwbGF5ZXJfbmFtZSkgJT4lIA0KICBkcGx5cjo6c3VtbWFyaXplKA0KICAgIE5fYmF0dHV0ZSA9IG4oKSwNCiAgICBjb3VudF9wZXJmZXR0ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIiMiLCBuYS5ybSA9IFRSVUUpLA0KICAgIGNvdW50X3Bvc2l0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiKyIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X2VzY2FsYW1hdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIiEiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9uZWdhdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIi0iLCBuYS5ybSA9IFRSVUUpLA0KICAgIGNvdW50X2Vycm9yaSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIj0iLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBvc2l0aXZpdMOgID0gcGVyY2VudCgoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkvTl9iYXR0dXRlLCBkaWdpdHMgPSAwKSwNCiAgICBlZmZpY2llbnphID0gcGVyY2VudCgoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkvTl9iYXR0dXRlLCBkaWdpdHMgPSAwKSwNCiAgKQ0KDQpkYXRhX3Bsb3QgPC0gdGFibGVfZGF0YQ0KDQp0YWJsZV9kYXRhDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSBjdW11bGF0aXZlIHN0YXRpc3RpY3MgZm9yIHRoZSB0ZWFtDQp0ZWFtX3RvdGFsIDwtIHRhYmxlX2RhdGEgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBOX2JhdHR1dGUgPSBzdW0oTl9iYXR0dXRlKSwNCiAgICBjb3VudF9wZXJmZXR0ZSA9IHN1bShjb3VudF9wZXJmZXR0ZSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oY291bnRfcG9zaXRpdmUpLA0KICAgIGNvdW50X2Vycm9yaSA9IHN1bShjb3VudF9lcnJvcmkpLA0KICAgIHBvc2l0aXZpdMOgID0gcGVyY2VudChzdW0oY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkgLyBzdW0oTl9iYXR0dXRlKSwgZGlnaXRzID0gMCksDQogICAgZWZmaWNpZW56YSA9IHBlcmNlbnQoc3VtKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUgLSBjb3VudF9lcnJvcmkpIC8gc3VtKE5fYmF0dHV0ZSksIGRpZ2l0cyA9IDApDQogICkgJT4lDQogIG11dGF0ZShwbGF5ZXJfbmFtZSA9ICJUT1QuIFNxdWFkcmEiKSAgIyBBZGQgYSBwbGF5ZXJfbmFtZSBmb3IgdGhlIHRlYW0gdG90YWwgcm93DQoNCiMgQ29tYmluZSB0aGUgdGVhbSB0b3RhbCByb3cgd2l0aCB0aGUgb3JpZ2luYWwgdGFibGUgZGF0YQ0KdGFibGVfZGF0YV93aXRoX3RvdGFsIDwtIGJpbmRfcm93cyh0YWJsZV9kYXRhLCB0ZWFtX3RvdGFsKQ0KDQojIFByaW50IHRoZSB0YWJsZSB3aXRoIHRoZSB0ZWFtIHRvdGFsIHJvdw0KdGFibGVfZGF0YV93aXRoX3RvdGFsDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGthYmxlRXh0cmEpDQoNCiMgQXBwbHkgY3VzdG9tIENTUyBzdHlsaW5nIHRvIHRoZSBlbnRpcmUgdGFibGUNCiMgUmVvcmRlciBjb2x1bW5zIHRvIG1ha2UgcG9zaXRpdml0w6AgdGhlIHNlY29uZCBjb2x1bW4NCnRhYmxlX2RhdGEgPC0gdGFibGVfZGF0YV93aXRoX3RvdGFsICU+JQ0KICAjc2VsZWN0KC1lZmZpY2llbnphKSAlPiUNCiAgc2VsZWN0KDEsIDcsIGV2ZXJ5dGhpbmcoKSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0Kc3R5bGVkX3RhYmxlIDwtIHRhYmxlX2RhdGEgJT4lDQogIGthYmxlKCJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sdGFibGVfY2xhc3MgPSAnc3R5bGVkLXRhYmxlJywgaHRtbF9mb250ID0gJyJCZSBWaWV0bmFtIFBybyIsIHNhbnMtc2VyaWYnKSAlPiUNCiAgcm93X3NwZWMoOSwgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2x1bW5fc3BlYygyLCBiYWNrZ3JvdW5kID0gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMyksICJsaWdodGdyZWVuIixpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjI1KSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4zKSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpKQ0KDQogICNyb3dfc3BlYyh3aGljaCh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiAwLjIgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCAwLjMpLCBiYWNrZ3JvdW5kID0gInllbGxvdyIpICU+JQ0KICAjcm93X3NwZWMod2hpY2godGFibGVfZGF0YSRlZmZpY2llbnphIDw9IDAuMiksIGJhY2tncm91bmQgPSAibGlnaHRjb3JhbCIpDQoNCiMgU2F2ZSB0aGUgc3R5bGVkIHRhYmxlIHRvIGFuIEhUTUwgZmlsZQ0Kd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoc3R5bGVkX3RhYmxlKSwgIkJhdHR1dGFfdGFiLmh0bWwiKQ0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHBsb3RseSkNCg0KZmlnIDwtIHBsb3RfbHkoZGF0YV9wbG90LCANCiAgICAgICAgICAgICAgIHggPSB+cG9zaXRpdml0w6AqMTAwLCANCiAgICAgICAgICAgICAgIHkgPSB+ZWZmaWNpZW56YSoxMDAsDQogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCANCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsIA0KICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKCc8aT5QbGF5ZXI8L2k+OiAle3RleHR9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPlBvc2l0aXZpdMOgICglKTwvYj46ICV7eH0pJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPkVmZmljaWVuemEgKCUpPC9iPjogJXt5fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5CYXR0dXRhPC9iPjogJXttYXJrZXIuc2l6ZX08ZXh0cmE+PC9leHRyYT4nKSwNCiAgICAgICAgICAgICAgIGNvbG9yID0gfnBvc2l0aXZpdMOgLA0KICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gfk5fYmF0dHV0ZSwgc2l6ZW1vZGUgPSAiYXJlYSIsIHNpemVyZWYgPSAwLjAwNSwgb3BhY2l0eSA9IDAuNSksDQogICAgICAgICAgICAgICB0ZXh0ID0gfnBsYXllcl9uYW1lDQopDQoNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICdRdWFsaXTDoCBCYXR0dXRhJywNCiAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnUG9zaXRpdml0w6AnLCBzaG93Z3JpZCA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdFZmZpY2llbnphJywgc2hvd2dyaWQgPSBUUlVFKQ0KKQ0KDQpmaWcNCmBgYA0KDQpgYGB7cn0NCiMgQXNzdW1pbmcgeW91IGhhdmUgeW91ciBkYXRhIGZyYW1lICdkYXRhX3Bsb3QnIHdpdGggY29sdW1ucyAncGxheWVyX25hbWUnIGFuZCAnZWZmaWNpZW56YScNCg0KIyBDYWxjdWxhdGUgdGhlIGNvbG9yIGJhc2VkIG9uIGVmZmljaWVuemEgdmFsdWVzDQpkYXRhX3Bsb3QkY29sb3IgPC0gaWZlbHNlKGRhdGFfcGxvdCRlZmZpY2llbnphIDwgMC4yNSwgInJlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkYXRhX3Bsb3QkZWZmaWNpZW56YSA+IDAuMzAsICJncmVlbiIsICJ5ZWxsb3ciKSkNCg0KDQpmaWcgPC0gcGxvdF9seSgNCiAgeCA9IGRhdGFfcGxvdCRwbGF5ZXJfbmFtZSwNCiAgeSA9IHJvdW5kKGRhdGFfcGxvdCRlZmZpY2llbnphLCBkaWdpdHMgPSAyKSwNCiAgbWFya2VyID0gbGlzdChjb2xvciA9IGRhdGFfcGxvdCRjb2xvciksDQogIG5hbWUgPSAiRWZmaWNpZW56YSBpbiBCYXR0dXRhIiwNCiAgdHlwZSA9ICJiYXIiLA0KICB0ZXh0ID0gcm91bmQoZGF0YV9wbG90JGVmZmljaWVuemEsIGRpZ2l0cyA9IDIpLCB0ZXh0cG9zaXRpb24gPSAnYXV0bycsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAncmdiKDE1OCwyMDIsMjI1KScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYig4LDQ4LDEwNyknLCB3aWR0aCA9IDEuNSkpKQ0KDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAiRWZmaWNpZW56YSBpbiBCYXR0dXRhIiwNCg0KICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIiIpLA0KDQogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIikpDQoNCg0KZmlnDQoNCiMgQ3VzdG9taXplIG90aGVyIHBsb3Qgc2V0dGluZ3MgYXMgbmVlZGVkDQoNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoaHRtbHdpZGdldHMpDQoNCiMgQXNzdW1pbmcgJ2ZpZycgaXMgeW91ciBQbG90bHkgZmlndXJlDQpzYXZlV2lkZ2V0KGZpZywgIkJhdHR1dGEuaHRtbCIpDQpgYGANCg0KIyMgUmVuZGltZW50byBpbiBSaWNlemlvbmUNCg0KT3JhIGFuYWxpenppYW1vIGxhIHJpY2V6aW9uZToNCg0KYGBge3J9DQojLCBlbmRfem9uZSA9PSA1DQp0YWJsZV9kYXRhIDwtIHB4ICU+JSANCiAgZHBseXI6OmZpbHRlcihza2lsbCA9PSAiUmVjZXB0aW9uIiwgdGVhbSA9PSB0ZWFtTmFtZSkgJT4lIA0KICBncm91cF9ieShwbGF5ZXJfbmFtZSkgJT4lIA0KICBkcGx5cjo6c3VtbWFyaXplKA0KICAgIE5fcmVjZXB0aW9ucyA9IG4oKSwNCiAgICBjb3VudF9wZXJmZXR0ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIiMiLCBuYS5ybSA9IFRSVUUpLA0KICAgIGNvdW50X3Bvc2l0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiKyIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X2VzY2FsYW1hdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIiEiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9uZWdhdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIi0iLCBuYS5ybSA9IFRSVUUpLA0KICAgIGNvdW50X2Vycm9yaSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIj0iLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBvc2l0aXZpdMOgID0gcGVyY2VudCgoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkvTl9yZWNlcHRpb25zLCBkaWdpdHMgPSAwKSwNCiAgICBlZmZpY2llbnphID0gcGVyY2VudCgoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkvTl9yZWNlcHRpb25zLCBkaWdpdHMgPSAwKSwNCiAgKQ0KDQpkYXRhX3Bsb3QgPC0gdGFibGVfZGF0YQ0KDQp0YWJsZV9kYXRhDQpgYGANCg0KYGBge3J9DQojIENvbXB1dGUgdG90YWwgc3RhdGlzdGljcyBmb3IgdGhlIHRlYW0NCnRvdGFsX3N0YXRzIDwtIHRhYmxlX2RhdGEgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBOX3JlY2VwdGlvbnMgPSBzdW0oTl9yZWNlcHRpb25zKSwNCiAgICBjb3VudF9wZXJmZXR0ZSA9IHN1bShjb3VudF9wZXJmZXR0ZSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oY291bnRfcG9zaXRpdmUpLA0KICAgIGNvdW50X2Vycm9yaSA9IHN1bShjb3VudF9lcnJvcmkpLA0KICAgIHBvc2l0aXZpdMOgID0gcGVyY2VudChzdW0oY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkgLyBzdW0oTl9yZWNlcHRpb25zKSksDQogICAgZWZmaWNpZW56YSA9IHBlcmNlbnQoc3VtKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUgLSBjb3VudF9lcnJvcmkpIC8gc3VtKE5fcmVjZXB0aW9ucykpDQogICkgJT4lDQogIG11dGF0ZShwbGF5ZXJfbmFtZSA9ICJUT1QuIFNxdWFkcmEiKSAgIyBBZGQgYSBwbGF5ZXJfbmFtZSBmb3IgdGhlIHRlYW0gdG90YWwgcm93DQoNCiMgQ29tYmluZSB0aGUgdGVhbSB0b3RhbCByb3cgd2l0aCB0aGUgb3JpZ2luYWwgdGFibGUgZGF0YQ0KdGFibGVfZGF0YV93aXRoX3RvdGFsIDwtIGJpbmRfcm93cyh0YWJsZV9kYXRhLCB0b3RhbF9zdGF0cykNCg0KIyBQcmludCB0aGUgdGFibGUgd2l0aCB0aGUgdGVhbSB0b3RhbCByb3cNCnRhYmxlX2RhdGFfd2l0aF90b3RhbA0KYGBgDQoNCmBgYHtyfQ0KdGFibGVfZGF0YSA8LSB0YWJsZV9kYXRhX3dpdGhfdG90YWwgJT4lDQogICNzZWxlY3QoLWVmZmljaWVuemEpICU+JQ0KICBzZWxlY3QoMSwgNywgZXZlcnl0aGluZygpKQ0KDQojIEFwcGx5IGN1c3RvbSBDU1Mgc3R5bGluZyB0byB0aGUgZW50aXJlIHRhYmxlDQpzdHlsZWRfdGFibGUgPC0gdGFibGVfZGF0YSAlPiUNCiAga2FibGUoImh0bWwiKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGh0bWx0YWJsZV9jbGFzcyA9ICdzdHlsZWQtdGFibGUnLCBodG1sX2ZvbnQgPSAnIkJlIFZpZXRuYW0gUHJvIiwgc2Fucy1zZXJpZicpICU+JQ0KICByb3dfc3BlYyg3LCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDIsIA0KICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gY2FzZV93aGVuKA0KICAgICAgICAgICAgICAgICh0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJTZXJlbmEgU2NvZ25hbWlsbG8iIHwgdGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiRmVkZXJpY2EgRmVycmFyaW8iKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjU2KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjQ4KSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC41NiksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiTGVuYSBTdGlncm90IiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFubmEgSGFhayIpIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMzcpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzEpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjM3KSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpLA0KICAgICAgICAgICAgICAgICh0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbGljZSBUYW5hc2UiIHwgdGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiTWFkaXNvbiBLdWJpayIpIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDMpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzcpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjQzKSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpLA0KICAgICAgICAgICAgICAgIFRSVUUgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC40MiksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC40MSkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDIpLCAib3JhbmdlIiwgInJlZCIpKQ0KICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICkNCg0KIyBTYXZlIHRoZSBzdHlsZWQgdGFibGUgdG8gYW4gSFRNTCBmaWxlDQp3cml0ZUxpbmVzKGFzLmNoYXJhY3RlcihzdHlsZWRfdGFibGUpLCAiUmljZXppb25lX3RhYi5odG1sIikNCmBgYA0KDQpgYGB7cn0NCmZpZyA8LSBwbG90X2x5KGRhdGFfcGxvdCwgDQogICAgICAgICAgICAgICB4ID0gfnBvc2l0aXZpdMOgKjEwMCwgDQogICAgICAgICAgICAgICB5ID0gfmVmZmljaWVuemEqMTAwLA0KICAgICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgDQogICAgICAgICAgICAgICBtb2RlID0gJ21hcmtlcnMnLCANCiAgICAgICAgICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZSgnPGk+UGxheWVyPC9pPjogJXt0ZXh0fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5Qb3NpdGl2aXTDoCglKTwvYj46ICV7eH0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+RWZmaWNpZW56YSglKTwvYj46ICV7eX0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+UmljZXppb25lPC9iPjogJXttYXJrZXIuc2l6ZX08ZXh0cmE+PC9leHRyYT4nKSwNCiAgICAgICAgICAgICAgIGNvbG9yID0gfnBvc2l0aXZpdMOgLA0KICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gfk5fcmVjZXB0aW9ucywgc2l6ZW1vZGUgPSAiYXJlYSIsIHNpemVyZWYgPSAwLjAwNSwgb3BhY2l0eSA9IDAuNSksDQogICAgICAgICAgICAgICB0ZXh0ID0gfnBsYXllcl9uYW1lDQopDQoNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICdRdWFsaXTDoCBSaWNlemlvbmUnLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQb3NpdGl2aXTDoCcsIHNob3dncmlkID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0VmZmljaWVuemEnLCBzaG93Z3JpZCA9IFRSVUUpDQopDQpmaWcNCmBgYA0KDQpgYGB7cn0NCiMgQXNzdW1pbmcgeW91IGhhdmUgeW91ciBkYXRhIGZyYW1lICdkYXRhX3Bsb3QnIHdpdGggY29sdW1ucyAncGxheWVyX25hbWUnIGFuZCAnZWZmaWNpZW56YScNCg0KIyBDYWxjdWxhdGUgdGhlIGNvbG9yIGJhc2VkIG9uIGVmZmljaWVuemEgdmFsdWVzDQpkYXRhX3Bsb3QgPC0gdGFibGVfZGF0YSAlPiUNCiAgbXV0YXRlKGNvbG9yID0gY2FzZV93aGVuKA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNlcmVuYSBTY29nbmFtaWxsbyIsICJGZWRlcmljYSBGZXJyYXJpbyIpICYgZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNTYpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkxlbmEgU3RpZ3JvdCIsICJBbm5hIEhhYWsiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjM3KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDMpKSB8DQogICAgZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDIpIH4gImxpZ2h0Z3JlZW4iLA0KICAgIA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNlcmVuYSBTY29nbmFtaWxsbyIsICJGZWRlcmljYSBGZXJyYXJpbyIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC40OCkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjU2KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJMZW5hIFN0aWdyb3QiLCAiQW5uYSBIYWFrIikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjMxKSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuMzcpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkFsaWNlIFRhbmFzZSIsICJNYWRpc29uIEt1YmlrIikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjM3KSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDMpKSB8DQogICAgKGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDEpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MikpIH4gIm9yYW5nZSIsDQogICAgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiU2VyZW5hIFNjb2duYW1pbGxvIiwgIkZlZGVyaWNhIEZlcnJhcmlvIikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC40OCkgKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkxlbmEgU3RpZ3JvdCIsICJBbm5hIEhhYWsiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjMxKSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA8PSBwZXJjZW50KDAuMzcpKSB8DQogICAgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MSkgfiAibGlnaHRjb3JhbCIsDQogICAgDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgKSkNCg0KDQoNCmZpZyA8LSBwbG90X2x5KA0KICB4ID0gZGF0YV9wbG90JHBsYXllcl9uYW1lLA0KICB5ID0gcm91bmQoZGF0YV9wbG90JGVmZmljaWVuemEsIGRpZ2l0cyA9IDIpLA0KICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gZGF0YV9wbG90JGNvbG9yKSwNCiAgbmFtZSA9ICJFZmZpY2llbnphIGluIFJpY2V6aW9uZSIsDQogIHR5cGUgPSAiYmFyIiwNCiAgdGV4dCA9IHJvdW5kKGRhdGFfcGxvdCRlZmZpY2llbnphLCBkaWdpdHMgPSAyKSwgdGV4dHBvc2l0aW9uID0gJ2F1dG8nLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ3JnYigxNTgsMjAyLDIyNSknLCBsaW5lID0gbGlzdChjb2xvciA9ICdyZ2IoOCw0OCwxMDcpJywgd2lkdGggPSAxLjUpKSkNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIkVmZmljaWVuemEgaW4gUmljZXppb25lIiwNCg0KICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIiIpLA0KDQogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIikpDQoNCg0KZmlnDQpgYGANCg0KYGBge3J9DQpzYXZlV2lkZ2V0KGZpZywgIlJpY2V6aW9uZS5odG1sIikNCmBgYA0KDQojIyBSZW5kaW1lbnRvIGluIEF0dGFjY28NCg0KYGBge3J9DQojICBlbmRfem9uZSA9PSA1DQp0YWJsZV9kYXRhIDwtIHB4ICU+JSANCiAgZHBseXI6OmZpbHRlcihza2lsbCA9PSAiQXR0YWNrIiwgdGVhbSA9PSB0ZWFtTmFtZSkgJT4lIA0KICBncm91cF9ieShwbGF5ZXJfbmFtZSkgJT4lIA0KICBkcGx5cjo6c3VtbWFyaXplKA0KICAgIE5fYXR0YWNrcyA9IG4oKSwNCiAgICBjb3VudF9wZXJmZXR0ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIiMiLCBuYS5ybSA9IFRSVUUpLA0KICAgIGNvdW50X3Bvc2l0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiKyIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X2VzY2FsYW1hdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIiEiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9uZWdhdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIi0iLCBuYS5ybSA9IFRSVUUpLA0KICAgIGNvdW50X2Vycm9yaSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIj0iLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBvc2l0aXZpdMOgID0gcGVyY2VudCgoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkvTl9hdHRhY2tzLCBkaWdpdHMgPSAwKSwNCiAgICBlZmZpY2llbnphID0gcGVyY2VudCgoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkvTl9hdHRhY2tzLCBkaWdpdHMgPSAwKSwNCiAgKQ0KDQpkYXRhX3Bsb3QgPC0gdGFibGVfZGF0YQ0KDQp0YWJsZV9kYXRhDQpgYGANCg0KYGBge3J9DQojIENvbXB1dGUgdGhlIHRvdGFsIHN0YXRpc3RpY3MNCnRvdGFsX3N0YXRzIDwtIHRhYmxlX2RhdGEgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBOX2F0dGFja3MgPSBzdW0oTl9hdHRhY2tzKSwNCiAgICBjb3VudF9wZXJmZXR0ZSA9IHN1bShjb3VudF9wZXJmZXR0ZSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oY291bnRfcG9zaXRpdmUpLA0KICAgIGNvdW50X2Vycm9yaSA9IHN1bShjb3VudF9lcnJvcmkpLA0KICAgIHBvc2l0aXZpdMOgID0gcGVyY2VudChzdW0oY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkgLyBzdW0oTl9hdHRhY2tzKSksDQogICAgZWZmaWNpZW56YSA9IHBlcmNlbnQoc3VtKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUgLSBjb3VudF9lcnJvcmkpIC8gc3VtKE5fYXR0YWNrcykpDQogICkgJT4lDQogIG11dGF0ZShwbGF5ZXJfbmFtZSA9ICJUT1QuIFNxdWFkcmEiKSAgIyBBZGQgYSBwbGF5ZXJfbmFtZSBmb3IgdGhlIHRlYW0gdG90YWwgcm93DQoNCiMgQWRkIHRoZSB0b3RhbCByb3cgdG8gdGhlIHRhYmxlIGRhdGENCnRhYmxlX2RhdGEgPC0gYmluZF9yb3dzKHRhYmxlX2RhdGEsIHRvdGFsX3N0YXRzKQ0KYGBgDQoNCmBgYHtyfQ0KdGFibGVfZGF0YSA8LSB0YWJsZV9kYXRhICU+JQ0KICAjc2VsZWN0KC1lZmZpY2llbnphKSAlPiUNCiAgc2VsZWN0KDEsIDcsIGV2ZXJ5dGhpbmcoKSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0Kc3R5bGVkX3RhYmxlIDwtIHRhYmxlX2RhdGEgJT4lDQogIGthYmxlKCJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sdGFibGVfY2xhc3MgPSAnc3R5bGVkLXRhYmxlJywgaHRtbF9mb250ID0gJyJCZSBWaWV0bmFtIFBybyIsIHNhbnMtc2VyaWYnKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgYmFja2dyb3VuZCA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQW5uYSBBZGVsdXNpIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI3KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjI0KSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4yNyksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQW5uYSBIYWFrIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkxlbmEgU3RpZ3JvdCIpIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMzQpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzApICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjM0KSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpLA0KICAgICAgICAgICAgICAgICh0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbGljZSBUYW5hc2UiIHwgdGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiTWFkaXNvbiBLdWJpayIpIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMjQpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuMjApICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjI0KSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpLA0KICAgICAgICAgICAgICAgICh0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJTYWx5IFRoaW9yIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFtYW5kaGEgU3lsdmVzIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFubmEgSGFsbCIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJCZWF0cmljZSBNb2xpbmFybyIpIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDQpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzgpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjQ0KSwgInllbGxvdyIsICJsaWdodGNvcmFsIikpLA0KICAgICAgICAgICAgICAgIFRSVUUgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC40MiksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC40MSkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDIpLCAib3JhbmdlIiwgInJlZCIpKQ0KICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICkNCg0KIyBTYXZlIHRoZSBzdHlsZWQgdGFibGUgdG8gYW4gSFRNTCBmaWxlDQp3cml0ZUxpbmVzKGFzLmNoYXJhY3RlcihzdHlsZWRfdGFibGUpLCAiQXR0YWNjb190YWIuaHRtbCIpDQpgYGANCg0KYGBge3J9DQpmaWcgPC0gcGxvdF9seShkYXRhX3Bsb3QsIA0KICAgICAgICAgICAgICAgeCA9IH5wb3NpdGl2aXTDoCwgDQogICAgICAgICAgICAgICB5ID0gfmVmZmljaWVuemEsDQogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCANCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsIA0KICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKCc8aT5QbGF5ZXI8L2k+OiAle3RleHR9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPlBvc2l0aXZpdMOgKCUpPC9iPjogJXt4fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5FZmZpY2llbnphKCUpPC9iPjogJXt5fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5BdHRhY2NoaTwvYj46ICV7bWFya2VyLnNpemV9PGV4dHJhPjwvZXh0cmE+JyksDQogICAgICAgICAgICAgICBjb2xvciA9IH5wb3NpdGl2aXTDoCwNCiAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IH5OX2F0dGFja3MsIHNpemVtb2RlID0gImFyZWEiLCBzaXplcmVmID0gMC4wMDUsIG9wYWNpdHkgPSAwLjUpLA0KICAgICAgICAgICAgICAgdGV4dCA9IH5wbGF5ZXJfbmFtZQ0KKQ0KDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAnUXVhbGl0w6AgQXR0YWNjbycsDQogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1Bvc2l0aXZpdMOgJywgc2hvd2dyaWQgPSBGQUxTRSksDQogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0VmZmljaWVuemEnLCBzaG93Z3JpZCA9IEZBTFNFKQ0KKQ0KDQpmaWcNCmBgYA0KDQpgYGB7cn0NCiMgQWRkIGEgbmV3IGNvbHVtbiAnY29sb3InIGJhc2VkIG9uIHRoZSBydWxlcyBwcm92aWRlZA0KZGF0YV9wbG90IDwtIHRhYmxlX2RhdGEgJT4lDQogIG11dGF0ZShjb2xvciA9IGNhc2Vfd2hlbigNCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEFkZWx1c2kiLCAiVGVycnkgUnV0aCBFbndlb253dSIpICYgZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMjcpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkFubmEgSGFhayIsICJMZW5hIFN0aWdyb3QiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjM0KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuMjQpKSB8DQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiU2FseSBUaGlvciIsICJBbWFuZGhhIFN5bHZlcyIsICJBbm5hIEhhbGwiLCAiQmVhdHJpY2UgTW9saW5hcm8iKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQ0KSkgfA0KICAgIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQyKSB+ICJsaWdodGdyZWVuIiwNCiAgICANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEFkZWx1c2kiLCAiVGVycnkgUnV0aCBFbndlb253dSIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4yNCkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjI3KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEhhYWsiLCAiTGVuYSBTdGlncm90IikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjMwKSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuMzQpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkFsaWNlIFRhbmFzZSIsICJNYWRpc29uIEt1YmlrIikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjIwKSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuMjQpKSB8DQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiU2FseSBUaGlvciIsICJBbWFuZGhhIFN5bHZlcyIsICJBbm5hIEhhbGwiLCAiQmVhdHJpY2UgTW9saW5hcm8iKSAmIGVmZmljaWVuemEgPiBwZXJjZW50KDAuMzgpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40NCkpIHwNCiAgICAoZWZmaWNpZW56YSA+IHBlcmNlbnQoMC40MSkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjQyKSkgfiAib3JhbmdlIiwNCiAgICANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEFkZWx1c2kiLCAiVGVycnkgUnV0aCBFbndlb253dSIpICYgZWZmaWNpZW56YSA8PSBwZXJjZW50KDAuMjQpKSB8IA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIkFubmEgSGFhayIsICJMZW5hIFN0aWdyb3QiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjMwKSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA8PSBwZXJjZW50KDAuMjApKSB8DQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiU2FseSBUaGlvciIsICJBbWFuZGhhIFN5bHZlcyIsICJBbm5hIEhhbGwiLCAiQmVhdHJpY2UgTW9saW5hcm8iKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjM4KSkgfA0KICAgIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDEpIH4gImxpZ2h0Y29yYWwiLA0KICAgIA0KICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICkpDQoNCg0KZmlnIDwtIHBsb3RfbHkoDQogIHggPSBkYXRhX3Bsb3QkcGxheWVyX25hbWUsDQogIHkgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksDQogIG1hcmtlciA9IGxpc3QoY29sb3IgPSBkYXRhX3Bsb3QkY29sb3IpLA0KICBuYW1lID0gIkVmZmljaWVuemEgaW4gQXR0YWNjbyIsDQogIHR5cGUgPSAiYmFyIiwNCiAgdGV4dCA9IHJvdW5kKGRhdGFfcGxvdCRlZmZpY2llbnphLCBkaWdpdHMgPSAyKSwgdGV4dHBvc2l0aW9uID0gJ2F1dG8nLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ3JnYigxNTgsMjAyLDIyNSknLCBsaW5lID0gbGlzdChjb2xvciA9ICdyZ2IoOCw0OCwxMDcpJywgd2lkdGggPSAxLjUpKSkNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIkVmZmljaWVuemEgaW4gQXR0YWNjbyIsDQoNCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICIiKSwNCg0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIiIpKQ0KDQoNCmZpZw0KYGBgDQoNCmBgYHtyfQ0Kc2F2ZVdpZGdldChmaWcsICJBdHRhY2NvLmh0bWwiKQ0KYGBgDQoNCiMjIFZvbGxleWJhbGwgRGlzdHJpYnV0aW9uDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCg0KZm9yIChpIGluIDE6Nikgew0KICBhdHRhY2tfcmF0ZSA8LSBweCAlPiUgDQogICAgZHBseXI6OmZpbHRlcihza2lsbCA9PSAiQXR0YWNrIiwgdGVhbSA9PSB0ZWFtTmFtZSwgdmlzaXRpbmdfc2V0dGVyX3Bvc2l0aW9uID09IGkpICU+JQ0KICAgIGdyb3VwX2J5KHN0YXJ0X3pvbmUpICU+JSANCiAgICBkcGx5cjo6c3VtbWFyaXplKG5fYXR0YWNrcyA9IG4oKSkgJT4lDQogICAgbXV0YXRlKHJhdGUgPSBuX2F0dGFja3Mvc3VtKG5fYXR0YWNrcykpICU+JSANCiAgICB1bmdyb3VwDQoNCiAgYXR0YWNrX3JhdGUgPC0gY2JpbmQoYXR0YWNrX3JhdGUsIGR2X3h5KGF0dGFja19yYXRlJHN0YXJ0X3pvbmUsIGVuZCA9ICJsb3dlciIpKQ0KDQogIHRtMmkgPC0gYXR0YWNrX3JhdGUkdGVhbSA9PSB0ZWFtcyhweClbMl0NCiAgYXR0YWNrX3JhdGVbdG0yaSwgYygieCIsICJ5IildIDwtIGR2X2ZsaXBfeHkoYXR0YWNrX3JhdGVbdG0yaSwgYygieCIsICJ5IildKQ0KDQogIGF0dGFja19yYXRlJHJhdGVfcm91bmRlZCA8LSByb3VuZChhdHRhY2tfcmF0ZSRyYXRlLCAyKQ0KDQogIGZpZzIgPC0gZ2dwbG90KGF0dGFja19yYXRlLCBhZXMoeCwgeSwgZmlsbCA9IHJhdGVfcm91bmRlZCkpICsgDQogICAgZ2VvbV90aWxlKCkgKyANCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKHJhdGVfcm91bmRlZCAqIDEwMCwgMiksICIlIikpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzKSArDQogICAgZ2djb3VydChsYWJlbHMgPSB0ZWFtcyhweCkpICsNCiAgICBzY2FsZV9maWxsX2dyYWRpZW50MihuYW1lID0gIkF0dGFjayByYXRlICglKSIsIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkNCg0KICBodG1sX25hbWUgPC0gcGFzdGUwKCJEaXN0cmlidXppb25lIiwgaSwgIi5wbmciKQ0KICANCiAgIyBTYXZlIHRoZSBwbG90IGFzIGFuIEhUTUwgZmlsZQ0KICBnZ3NhdmUoaHRtbF9uYW1lLCBwbG90ID0gZmlnMiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsIGRwaSA9IDMwMCkNCn0NCg0KYGBgDQo=